﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;

using ZetaPrints.ZetaClient.Shared;
using ZetaPrints.ZetaClient.Shared.Manipulator;

namespace ZetaPrints.ZetaClient.Data
{
    /// <summary>
    /// This will be the data layer to handle all Web interactions on the 
    /// application. No Business logic should be here
    /// </summary>
    public class WebAction
    {
        #region public properties
        /// <summary>
        /// Site to connect to should be in only the 
        /// piece that matters. No http:// only the 
        /// customerWebName.zetaprints.com
        /// </summary>
        public string SiteName { get; private set; }
        /// <summary>
        /// Gets the current ApiKey set
        /// </summary>
        public string ApiKey { get; private set; }
        /// <summary>
        /// Gets the Http Status of the last communication
        /// </summary>
        public HttpStatusCode? CommStatus { get; private set; }
        /// <summary>
        /// Gets the data that was returned on the last communication
        /// </summary>
        public string Data { get; private set; }
        #endregion


        #region event
        public event EventHandler<EventMessenger> OnEventMessenger;
        #endregion


        #region constructors
        /// <summary>
        /// Initializes the WebAction. From this point forward we can just connect 
        /// for one customer. If the need arrises to use another we need to reinialize
        /// this class
        /// </summary>
        /// <param name="site"></param>
        /// <param name="apiKey"></param>
        public WebAction(string siteName, Guid apiKey)
        {
            SiteName = siteName;
            ApiKey = apiKey.ToString();

            CommStatus = null;
            Data = string.Empty;
        }
        #endregion


        #region private methods
        /// <summary>
        /// Little private method to send our messages
        /// </summary>
        /// <param name="body"></param>
        private void SendMessage(MessageTypeOption messageType, string message)
        {
            /// is there anything listening here?
            if (OnEventMessenger != null)
            {
                OnEventMessenger(this, new EventMessenger(messageType, message));
            }
        }
        /// <summary>
        /// Returns a Uri which contains the Site the WebAction Command 
        /// is ready for some parameters to be added
        /// </summary>
        /// <param name="webActionCommand"></param>
        /// <returns></returns>
        private string CreateCommandUri(string webActionCommand, params string[] parameters)
        {
            string uriString = CreateBaseUri() +
                "/api.aspx?page=" + webActionCommand +
                ";ApiKey=" + ApiKey;

            foreach (string currentParameter in parameters)
            {
                uriString += ";" + currentParameter;
            }

            return uriString;
        }
        /// <summary>
        /// Gets the very base of the Uri for this system
        /// </summary>
        /// <returns></returns>
        private string CreateBaseUri()
        {
            return "http://" + SiteName;
        }
        /// <summary>
        /// Connect to the server do a POST GET and retrieve the information
        /// which received. Will return TRUE if information can be found under
        /// the _data attribute
        /// </summary>
        /// <param name="uriString">The http command for information to be fetched from</param>
        /// <returns></returns>
        private bool Fetch(string uriString)
        {
            bool goodData = false;

            Data = string.Empty;

            /// create our resquest to go out
            HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create(new Uri(uriString));

            SendMessage(MessageTypeOption.Message, "Fetching information from the web");

                /// stay inside the using this way it will be sure to be destroyed
                using (HttpWebResponse httpWebResponseResponse = (HttpWebResponse)httpWebRequest.GetResponse())
                {
                    /// check if we got it
                    switch (httpWebResponseResponse.StatusCode)
                    {
                        case HttpStatusCode.OK:
                        case HttpStatusCode.Created:
                        case HttpStatusCode.Accepted:
                        case HttpStatusCode.NonAuthoritativeInformation:
                        case HttpStatusCode.NoContent:
                        case HttpStatusCode.ResetContent:
                        case HttpStatusCode.PartialContent:
                            goodData = true;
                            break;
                        default:
                            /// in case they have added new enumerations for a 
                            /// good http Status code
                            if ((((int)httpWebResponseResponse.StatusCode) >= 200) ||
                                (((int)httpWebResponseResponse.StatusCode) <= 299))
                            {
                                goodData = true;
                            }
                            break;
                    }

                    /// do we get the data, we only do this
                    /// if we got some good data
                    if (goodData)
                    {
                        Stream responseStream = httpWebResponseResponse.GetResponseStream();

                        ZetaClientConfiguration zetaClientConfiguration = new ZetaClientConfiguration();

                        using (StreamReader reader = new StreamReader(responseStream, Encoding.Default))
                        {
                            Data = reader.ReadToEnd();
#if DEBUG
                            if (!Directory.Exists(zetaClientConfiguration.DownloadRootDirectory))
                            {
                                Directory.CreateDirectory(zetaClientConfiguration.DownloadRootDirectory);
                            }

                            Monitor.Enter(zetaClientConfiguration.DownloadRootDirectory + @"\" + "debugFile.xml");

                            try
                            {
                                using (StreamWriter sw = new StreamWriter(zetaClientConfiguration.DownloadRootDirectory + @"\" + "debugFile.xml", true))
                                {
                                    sw.WriteLine(DateTime.Now + Data);
                                }
                            }
                            catch
                            {
                                Monitor.Exit(zetaClientConfiguration.DownloadRootDirectory + @"\" + "debugFile.xml");
                            }
#endif
                        }
                    }
                    else
                    {
                        SendMessage(MessageTypeOption.Message, "Fetch failed");
                    }
                }

            return goodData;
        }
        #endregion


        #region public methods
        #region get
        /// <summary>
        /// Gets the OrderList from the server. Returns true if information was fetched.
        /// This function is designed to be lightweight as it needs to be called often. 
        /// The response contains a list of no more than 100 orders sorted by date, ascended. 
        /// Once you get the first 100, note the date and time of the last one and adjust 
        /// DateFrom parameter accordingly to get next 100 orders.
        /// Example:http://posters.example.com/api.aspx?page=api-order-list;ApiKey=532525b4-10fe-4424-bf8f-890cb8bd57a1;DateFrom=2008-01-01T00:00:00
        /// </summary>
        /// <param name="startDateTime">The start date from where the orders should be retrieved from.</param>
        /// <returns>True if the data was received from the server. False if an http error had occured</returns>
        public bool GetOrderList(DateTime startDateTime)
        {
            SendMessage(MessageTypeOption.Message, "Starting to get order list.");

            string uriString = CreateCommandUri("api-order-list",
                "DateFrom=" + startDateTime.ToString("yyyy-MM-dd") + "T" + startDateTime.ToString("HH:mm:ss"));

            /// fetches the data
            return Fetch(uriString);
        }
        /// <summary>
        /// Gets the detail of an order.
        /// The response contains most of order details stored in the database, including user input 
        /// and images. Some internal technical parameters of the files are omitted. Once all the 
        /// order details are known to your client application it should make decisions on which 
        /// resources to download.
        /// 
        /// Order details do not change, except for payment, status and comments. Saved orders have 
        /// no output files generated. If the user completes a saved order it is likely to be deleted 
        /// and replaced with a new order with a different ID.
        /// 
        /// All resources referenced from order details are static and should be cached forever.
        /// </summary>
        /// <param name="orderId"></param>
        /// <returns></returns>
        public bool GetOrderDetail(Guid orderId)
        {
            SendMessage(MessageTypeOption.Message, "Starting to get order detail");

            string uriString = CreateCommandUri("api-order",
                "OrderID=" + orderId.ToString());

            return Fetch(uriString);
        }
        /// <summary>
        /// Gets the file name requested, this file name can also contain 
        /// directories.
        /// </summary>
        /// <param name="fileName"></param>
        /// <returns></returns>
        public bool GetFile(string fileName)
        {
            bool gotFile = false;

            if (fileName != null)
            {
                SendMessage(MessageTypeOption.Message, "Starting to get file:" + fileName);

                string uriString = CreateBaseUri();

                if (!fileName.StartsWith("/"))
                {
                    fileName = "/" + fileName;
                }

                uriString += fileName;

                try
                {
                    gotFile = Fetch(uriString);
                }
                catch (System.Net.WebException ex)
                {
                    if (ex.Message.Contains("The remote server returned an error: (404) Not Found."))
                    {
                        gotFile = false;
                    }
                    else
                    {
                        throw;
                    }
                }
            }
            return gotFile;
        }
        #endregion
        #region set
        /// <summary>
        /// Set the status to a new status
        /// </summary>
        /// <param name="newStatus"></param>
        /// <param name="oldStatus"></param>
        public bool SetStatus(Guid orderId, string newStatus, string oldStatus)
        {
            SendMessage(MessageTypeOption.Message, Lang.ChangingStartingToGetOrderDetail);

            string uriString = CreateCommandUri("api-order-status",
                "OrderID=" + orderId.ToString(),
                "Status=" + newStatus,
                "StatusOld=" + oldStatus);

            return Fetch(uriString);
        }
        #endregion
        #region save
        /// <summary>
        /// Saves the file which was captured in the data
        /// </summary>
        /// <param name="fileName"></param>
        /// <returns></returns>
        public bool SaveData(string fileName)
        {
            bool dataSaved = true;

            SendMessage(MessageTypeOption.Message, "Saving file:" + fileName);

            try
            {
                File.WriteAllBytes(fileName, Encoding.Default.GetBytes(Data));
            }
            catch (Exception ex)
            {
                SendMessage(MessageTypeOption.Message, "Failed to save file:" + ex.Message);
            }

            return dataSaved;
        }
        #endregion
        #endregion
    }
}
